package de.herberlin.server.httpd.ssi;

import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.Vector;



public class BSssi {

	private Properties envVars=new Properties();
	
	public static final int CMD_NOECHO=0;
	public static final int CMD_ECHO=1;
	public static final int CMD_INCLUDE=2;
	public static final int CMD_IF_TRUE=3;
	public static final int CMD_IF_FALSE=4;
    public static final int CMD_EXEC=5;
    public static final int CMD_FILESIZE=6;
    public static final int CMD_FLASTMOD=7;
    public static final int CMD_CGI=8;
    
	
	/**	Parsed if condition */
	private boolean ifState=true;	
	
	private DateFormat df =
		new SimpleDateFormat(BSssi.SSI_DATE_FORMAT, Locale.ENGLISH);

	public BSssi() {
	
	}
	
	public String format(Date date, TimeZone timeZone){
		DateFormat dateFormat;
		if (javaFormatString!=null){
			dateFormat=new SimpleDateFormat(javaFormatString,Locale.ENGLISH);
		} else {
			dateFormat=df;
		}
		if (timeZone!=null){
			dateFormat.setTimeZone(timeZone);
		}
		return dateFormat.format(date);
	}
	public String getErrorMessage() {
		return envVars.getProperty("errmsg","[Error processing SSI.]");
	}
	
	public void addEnvironment(String[] variables) {
		
		StringTokenizer st;
		
		for (int i=0;i< variables.length; i++) {
			st=new StringTokenizer(variables[i],"=");
			if (st.countTokens()!= 2) continue;
			envVars.put((String) st.nextElement(),(String)st.nextElement());
		}
		
		// some ssi properties
		envVars.put("errmsg","[Error processing SSI.]");
		df.setTimeZone(TimeZone.getDefault());
		envVars.put("DATE_LOCAL",df.format(new Date()));
		File aFile=new File(envVars.getProperty("PATH_TRANSLATED"));
		df.setTimeZone(TimeZone.getTimeZone("GMT"));
		envVars.put("DATE_GMT",df.format(new Date()));
		envVars.put("DOCUMENT_NAME",aFile.getName());
		//envVars.put("DOCUMENT_URI","http://"+envVars.getProperty("SERVER_NAME")+envVars.getProperty("PATH_INFO"));
		envVars.put("DOCUMENT_URI",envVars.getProperty("PATH_INFO"));
		envVars.put("SCRIPT_NAME",envVars.getProperty("PATH_INFO"));
		envVars.put("LAST_MODIFIED",df.format(new Date(aFile.lastModified())));
		envVars.put("sizefmt","bytes");
		
	}
	
	public Vector parse(String aTag) throws ParseException {
	
		String startTag="<!--";
		String endTag="-->";
		
		Vector theCommands=new Vector();
		Vector theStrings=new Vector();
		int start,end;
		
		// parsing command line into theStrings
		while (aTag.length()>0) {
			start=aTag.indexOf(startTag);
			end=aTag.indexOf(endTag);
			if (start> 0) {
				theStrings.addElement(aTag.substring(0,start));
				aTag=aTag.substring(start).trim();
			} else if(start>-1) {			
				theStrings.addElement(aTag.substring(start+startTag.length(),end));
				aTag=aTag.substring(end+endTag.length()).trim();
			} else {
				theStrings.addElement(aTag);
				aTag="";
			}			
		}
		
		// Building command
		Enumeration en=theStrings.elements();
		while (en.hasMoreElements()) {
			theCommands.addElement(buildCommand((String) en.nextElement()));
		}
		
		return theCommands;
	}

	private SSICommand buildCommand(String t) throws ParseException  {
	
		t.trim();
		if ( (t.indexOf("#if")==0) || (t.indexOf("#elif")==0) 
			|| (t.indexOf("#elseif")==0) || (t.indexOf("#else if")==0) ) {
				
			try {
				ifState=Expr.parse(replaceVars(getRight(t)));	
			}catch (ExprException ex) {
				throw new ParseException(ex.getMessage(),0);
			}
			int cmd=(ifState)?CMD_IF_TRUE:CMD_IF_FALSE;
			return new SSICommand(cmd,"","");
		} else if (t.indexOf("#else")==0) {
			ifState=!ifState;	
			int cmd=(ifState)?CMD_IF_TRUE:CMD_IF_FALSE;
			return new SSICommand(cmd,"","");
		} else if (t.indexOf("#endif")==0) {
			ifState=true;
			return new SSICommand(CMD_IF_TRUE,"","");
		}
		if (!ifState) return new SSICommand(CMD_NOECHO,"","");
				
		if(t.indexOf("#echo")==0) {
			String variableValue=getRight(t);
			if (variableValue.startsWith("$")) {
				variableValue="(none)";
			}
			return new SSICommand(CMD_ECHO,0,variableValue);
		
		} else if (t.indexOf("#printenv")==0) {
			return new SSICommand(CMD_ECHO,0,printEnv());
		
		} else if(t.indexOf("#include")==0) {
            if (getLeft(t).indexOf("virtual")==0) {
                return new SSICommand(CMD_CGI,getFileType(getRight(t)),getFullName(getRight(t)));
            } else {
                return new SSICommand(CMD_INCLUDE,getFileType(getRight(t)),getFullName(getRight(t)));
            }
		} else if(t.indexOf("#exec")==0) {
			if (getLeft(t).indexOf("cmd")==0) {
				return new SSICommand(CMD_EXEC,0,getRight(t)); 
			} else {
				return new SSICommand(CMD_CGI,getFileType(getRight(t)),getRight(t));
			}
		} else if (t.indexOf("#config")==0) {
			if (getLeft(t).equals("timefmt")) {
				newTimeformat(getRight(t));
			} else {
				envVars.put(getLeft(t),getRight(t));
			}
			return new SSICommand(CMD_NOECHO,"","");
		
		} else if (t.indexOf("#set")==0) {
			String key=getVar(t);
			String val=getValue(t);
			if (val!=null && val.length()>0) {
				envVars.put(key,val);
			} else {
				envVars.remove(key);
			}
			return new SSICommand(CMD_NOECHO,"","");
		} else if (t.indexOf("#fsize")==0) {
			return new SSICommand(CMD_FILESIZE,envVars.getProperty("sizefmt"),getFullName(getRight(t)));
		} else if (t.indexOf("#flastmod")==0) {
			return new SSICommand(CMD_FLASTMOD,"",getFullName(getRight(t)));
		}
		
		return new SSICommand(CMD_ECHO,0,t);
	}
	private String javaFormatString=null;

	public static final String SSI_DATE_FORMAT = "EEEE, dd-MMM-yyyy HH:mm:ss z";
	private void newTimeformat(String cStyleFormatString) {
		DateFormatExchanger exchanger=new DateFormatExchanger();
		javaFormatString = exchanger.cToJava(cStyleFormatString);
		if (javaFormatString==null) return;
		SimpleDateFormat format=new SimpleDateFormat(javaFormatString,Locale.ENGLISH);
		format.setTimeZone(TimeZone.getDefault());
		envVars.put("DATE_LOCAL",format.format(new Date()));
		format.setTimeZone(TimeZone.getTimeZone("GMT"));
		envVars.put("DATE_GMT",format.format(new Date()));
			
	}
	
	/**	Replaces $variables by their content */
	private String replaceVars(String t) {
		Enumeration en=envVars.keys();
		String currentKey; // current variable name to search
		int pos;	// position where a key was found
		while (en.hasMoreElements()) {
			currentKey=(String) en.nextElement();
			while( (pos=t.indexOf("$"+currentKey)) >-1) {
				t=t.substring(0,pos)+envVars.getProperty(currentKey)+t.substring(pos+currentKey.length()+1);
			}
		}//~ while moreElements
		return t;
	}
	
    /**
     */
	private String getFullName(String t) {
        
        /*
		String scriptName=envVars.getProperty("SCRIPT_NAME");
        if (scriptName!=null) {
            int pos=scriptName.indexOf(envVars.getProperty("DOCUMENT_NAME"));
            if (pos>0) {
                String fullPath=scriptName.substring(0,pos)+t;
		        return fullPath;
            }
        }
        return t;
        */
        t=replaceEnvVars(t);
        try {
            URI uri=new URI(envVars.getProperty("SCRIPT_NAME")); 
            uri=uri.resolve(t);
            return uri.getPath();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        return t;
	}
	
    /**
     * Replaces all $ENV_VARS  in source with their content.
     * @param source
     * @return the parsed string
     */
    private String replaceEnvVars(String source) {
        Enumeration en=envVars.keys();
        while (en.hasMoreElements()) {
            String key=(String) en.nextElement();
            if (source.indexOf("$"+key)!=-1) {
                source=source.replaceAll("\\$"+key,envVars.getProperty(key));   
            }
        }
        return source;  
    }
	private String getFileType(String t) {
		String ft="txt";
		int dot=t.lastIndexOf(".");
		if (dot>-1) {
			ft=t.substring(dot+1);
		}
		return ft;
	}
	
	private String printEnv() {
		
		StringBuffer sb=new StringBuffer("\n");
		String t;
	    Enumeration en=envVars.keys();
	    while (en.hasMoreElements()) {
	    	t=(String) en.nextElement();
	    	sb.append(t+"="+envVars.getProperty(t)+"\n");	
	    }
	    return sb.toString();
	}
	
	private String getRight(String t) {
		int pos=t.indexOf("=");
		if (pos==-1) return "";
		t=t.substring(pos+1,t.length()).replace('\"',' ').trim();
		
		if (envVars.getProperty(t) != null) t=envVars.getProperty(t);
        
       
		// do not longer accept $ for variables
//		if (t.indexOf("$")==0 && envVars.getProperty(t.substring(1)) != null) {
//			t=envVars.getProperty(t.substring(1));
//		}
		return t;
    }
    
	private String getLeft(String t) {
		int pos0=t.indexOf(" ");
		int pos=t.indexOf("=");
		if (pos==-1) return "";
		t=t.substring(pos0,pos).replace('\"',' ').trim();
		return t;
    }
    
	private String getVar(String t) {
		int pos0=t.indexOf("var=")+5;
		int pos=t.indexOf("\"",pos0);
		if (pos==-1) return "";
		t=t.substring(pos0,pos).replace('\"',' ').trim();
		return t;
    }

	private String getValue(String t) {
		int pos0=t.indexOf("value=")+7;
		int pos=t.indexOf("\"",pos0);
		if (pos==-1) return "";
		t=t.substring(pos0,pos).replace('\"',' ').trim();
		return t;
    }
}